<template>
  <div class="container">
    <div id="model"></div>
    <button
      id="btns"
      :style="{ cursor: isDisabled ? '' : 'pointer' }"
      :disabled="isDisabled"
      type="isDisabled:"
      @click="toHomeView(1)"
    >
      主视角
    </button>
  </div>
</template>
<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js";
import TWEEN from "@tweenjs/tween.js";
export default {
  name: "ThreeModel",
  data() {
    return {
      isDisabled: true,
      scene: null,
      renderer: null,
      controls: null,
      light: null,
      light2: null,
      group: new THREE.Group(),
      composer: null, // 控制发光
      outlinePass: null,
      renderPass: null,
      // 选中的模型
      selectedObjects: [],
      mouse: new THREE.Vector2(),
      raycaster: new THREE.Raycaster(),
      tween: null,
      //定义模型架子的长度
      long: 100,
      //定义模型架子的宽度
      tall: 24,
      //定义模型架子横纵板的宽度
      thickness: 0.4,
      positionObj: null,
    };
  },
  computed: {},
  watch: {},
  created() {},
  mounted() {
    this.init();
  },
  methods: {
    //创建模型视图
    initScene() {
      this.scene = new THREE.Scene();
    },
    //初始化相机
    initCamera() {
      const { long, tall } = this;
      this.camera = new THREE.PerspectiveCamera(
        45,
        window.innerWidth / window.innerHeight,
        0.1,
        50000
      );

      this.camera.position.set(0, -(5 * tall) / 12, (6 * long) / 5);
    },
    //初始化灯光
    initLight() {
      let ambientLight = new THREE.AmbientLight(0x404040);
      this.scene.add(ambientLight);
      this.light = new THREE.DirectionalLight(0x333333);
      this.light.position.set(60, 30, 40);
      this.light2 = new THREE.DirectionalLight(0xdddddd);
      this.light2.position.set(-20, 20, -20);
      this.scene.add(this.light);
      this.scene.add(this.light2);
    },
    //加载渲染器
    initRender() {
      //dom元素渲染器
      this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
      this.renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染区域尺寸
      this.renderer.setClearColor(0x000000, 0); // 设置背景颜色
      //window.devicePixelRatio 当前设备的物理分辨率与css分辨率之比
      this.renderer.setPixelRatio(window.devicePixelRatio);
      //按层级先后渲染
      this.renderer.sortObjects = true;
      document.getElementById("model").appendChild(this.renderer.domElement);
    },
    //创建载入模型
    initModel() {
      const { long, tall, thickness } = this;
      //坐标系
      // let axes = new THREE.AxesHelper(4000);
      // this.scene.add(axes);

      //所有横板
      for (let index = 1; index <= tall / 4 - 1; index++) {
        let geometry = new THREE.BoxGeometry(long, thickness, 12);
        let material = new THREE.MeshLambertMaterial({
          color: 0x808080,
          opacity: 0.7,
          transparent: true,
        }); //材质对象Material
        let mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
        mesh.position.set(0, index * 4, 0); //设置mesh3模型对象的xyz坐标为120,0,0
        this.scene.add(mesh); //网格模型添加到场景中
      }
      //所有纵板
      for (let index = 1; index <= long / 4 + 1; index++) {
        let geometry = new THREE.BoxGeometry(thickness, tall, 12);
        let material = new THREE.MeshLambertMaterial({
          color: 0x808080,
          opacity: 0.7,
          transparent: true,
        }); //材质对象Material
        let mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
        mesh.position.set(-long / 2 + (index - 1) * 4, tall / 2, 0); //设置mesh3模型对象的xyz坐标为120,0,0
        this.scene.add(mesh); //网格模型添加到场景中
      }
      //正面所有箱子
      let list1 = new Array(tall / 4);
      for (let i = 0; i < list1.length; i++) {
        list1[i] = new Array(long / 4);
        //不一定写for循环赋值，还可以直接赋值，在数量有限的情况下
        for (let j = 0; j < long / 4; j++) {
          // a[i][j] = i + j;
          let geometry = new THREE.BoxGeometry(3, 3, 5);
          let material = new THREE.MeshLambertMaterial({
            color: 0x00b1f7,
            opacity: 0.4,
            transparent: true,
          });
          let mesh = new THREE.Mesh(geometry, material);
          mesh.name = i + 1 + "-" + (j + 1);
          mesh.position.set(-long / 2 + j * 4 + 2, i * 4 + 2, 3);
          this.scene.add(mesh);
        }
      }
      //背面所有箱子
      let list2 = new Array(tall / 4);
      for (let i = 0; i < list2.length; i++) {
        list2[i] = new Array(long / 4);
        //不一定写for循环赋值，还可以直接赋值，在数量有限的情况下
        for (let j = 0; j < long / 4; j++) {
          // a[i][j] = i + j;
          let geometry = new THREE.BoxGeometry(3, 3, 5);
          let material = new THREE.MeshLambertMaterial({
            color: 0x00b1f7,
            opacity: 0.4,
            transparent: true,
          });
          let mesh = new THREE.Mesh(geometry, material);

          mesh.name = "-" + (i + 1) + "-" + (j + 1);
          mesh.position.set(-long / 2 + j * 4 + 2, i * 4 + 2, -3);
          this.scene.add(mesh);
        }
      }
    },
    //高亮显示模型（呼吸灯）
    outlineObj(selectedObjects) {
      // 创建一个EffectComposer（效果组合器）对象，然后在该对象上添加后期处理通道。
      this.composer = new EffectComposer(this.renderer);
      // 新建一个场景通道  为了覆盖到原理来的场景上
      this.renderPass = new RenderPass(this.scene, this.camera);
      this.composer.addPass(this.renderPass);
      // 物体边缘发光通道
      this.outlinePass = new OutlinePass(
        new THREE.Vector2(window.innerWidth, window.innerHeight),
        this.scene,
        this.camera,
        selectedObjects
      );
      this.outlinePass.edgeStrength = 8.0; // 高光边缘边框的亮度
      this.outlinePass.edgeGlow = 1; // 光晕[0,1]  边缘微光强度
      this.outlinePass.usePatternTexture = false; // 是否使用父级的材质，纹理覆盖
      this.outlinePass.edgeThickness = 3; // 边框宽度，高光厚度
      this.outlinePass.downSampleRatio = 1; // 边框弯曲度
      this.outlinePass.pulsePeriod = 2; // 呼吸闪烁的速度，数值越大，律动越慢
      this.outlinePass.visibleEdgeColor.set(parseInt(0x00f6ff)); // 呼吸显示的颜色
      this.outlinePass.hiddenEdgeColor = new THREE.Color(0, 0, 0); // 呼吸消失的颜色
      // this.outlinePass.clear = true
      this.composer.addPass(this.outlinePass); // 加入高光特效
      this.outlinePass.selectedObjects = selectedObjects; // 需要高光的模型
    },
    // 鼠标点击模型
    onMouseClick(event) {
      //通过鼠标点击的位置计算出raycaster所需要的点的位置，以屏幕中心为原点，值的范围为-1到1
      this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
      this.mouse.y = -(event.clientY / (window.innerHeight - 50)) * 2 + 1;
      // 通过鼠标点的位置和当前相机的矩阵计算出raycaster
      this.raycaster.setFromCamera(this.mouse, this.camera);
      // 获取raycaster直线和所有模型相交的数组集合
      let intersects = this.raycaster.intersectObjects(this.scene.children);
      if (!intersects[0]) {
        return;
      } else {
        if (!intersects[0].object.name == "") {
          this.selectedObjects = [];
          this.selectedObjects.push(intersects[0].object);
          this.outlineObj(this.selectedObjects);
          this.positionObj = {
            x: intersects[0].object.position.x,
            y: intersects[0].object.position.y,
            z: intersects[0].object.position.z,
          };

          this.initTween(
            this.positionObj.x,
            this.positionObj.y,
            this.positionObj.z
          );
          this.isDisabled = false;

          this.controls.autoRotate = false;
        }
      }
    },
    // 相机移动动画
    initTween(targetX, targetY, targetZ) {
      // 需要保留this
      let initPosition = {
        x: this.camera.position.x,
        y: this.camera.position.y,
        z: this.camera.position.z,
      };
      let tween = new TWEEN.Tween(initPosition)
        .to({ x: targetX, y: targetY, z: targetZ }, 2000)
        .easing(TWEEN.Easing.Sinusoidal.InOut);
      let onUpdate = (pos) => {
        let x = pos.x;
        let y = pos.y;
        let z = pos.z;
        if (pos.z < 0) {
          this.camera.position.set(x, y, z - 12);
        } else {
          this.camera.position.set(x, y, z + 12);
        }
      };
      tween.onUpdate(onUpdate);
      tween.start();
      // this.controls.target.set(0, 0, 0);
      if (this.positionObj.z < 0) {
        this.controls.target.set(
          this.positionObj.x,
          this.positionObj.y - 0.4,
          -12
        );
      } else {
        this.controls.target.set(
          this.positionObj.x,
          this.positionObj.y - 0.4,
          12
        );
      }
    },
    toHomeView(id) {
      const { long, tall } = this;
      if (id === 1) {
        let initPosition = {
          x: this.camera.position.x,
          y: this.camera.position.y,
          z: this.camera.position.z,
        };
        let tween = new TWEEN.Tween(initPosition)
          .to({ x: 0, y: -(5 * tall) / 12, z: (6 * long) / 5 }, 2000)
          .easing(TWEEN.Easing.Sinusoidal.InOut);
        let onUpdate = (pos) => {
          let x = pos.x;
          let y = pos.y;
          let z = pos.z;
          this.camera.position.set(x, y, z);
        };
        tween.onUpdate(onUpdate);
        tween.start();
        this.controls.target.set(0, 0, 0);
        this.isDisabled = true;
        this.controls.autoRotate = true;
      }
    },
    //根据浏览器窗口自适应
    onWindowResize() {
      this.renderer.setSize(window.innerWidth, window.innerHeight);
      this.camera.aspect = window.innerWidth / window.innerHeight;
      this.camera.updateProjectionMatrix();
    },
    //使用OrbitControls控制三维场景缩放和旋转等功能
    initControls() {
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      //动态阻尼系数 即鼠标拖拽旋转的灵敏度
      this.controls.dampingFactor = 0.25;
      // this.controls.target.set(0, 900, 0)
      // //上下旋转范围
      this.controls.minPolarAngle = 0;
      this.controls.maxPolarAngle = 1.5;
      this.controls.autoRotate = true;
      //惯性滑动，滑动大小默认0.25
      this.controls.dampingFactor = 0.25;
      //滚轮是否可控制zoom，zoom速度默认1
      //缩放倍数
      this.controls.zoomSpeed = 1.0;
      //最大最小相机移动距离(景深相机)
      this.controls.minDistance = 1;
      this.controls.maxDistance = Infinity;
      //水平方向视角限制
      this.minAzimuthAngle = -Math.PI * 2;
      this.maxAzimuthAngle = Math.PI * 2;
      this.controls.enabledPan = true;
      this.keyPanSpeed = 7.0;
    },
    //运行动画
    animate() {
      TWEEN.update();
      this.renderer.render(this.scene, this.camera);
      this.controls.update();
      if (this.composer) {
        this.composer.render();
      }
      requestAnimationFrame(this.animate);
    },
    //初始化函数，页面加载完成是调用
    init() {
      this.initScene();
      this.initCamera();
      this.initLight();
      this.initRender();
      this.initModel();
      this.initControls();
      this.animate();
      window.onresize = this.onWindowResize;
      window.onclick = this.onMouseClick;
    },
  },
};
</script>

<style scoped>
.container {
  width: 100%;
  height: 100%;
  position: relative;
}
#btns {
  position: absolute;
  background-color: #031b34;
  bottom: 0;
  left: 50%;
  width: 80px;
  height: 30px;
  line-height: 30px;
  transform: translate(-50%, -50%);
  text-align: center;
  z-index: 9999;
  color: #00eeff;
  font-weight: bold;
  box-shadow: 0px 0px 2px 1px #00f6ff;
  border-radius: 6px;
  border: 1px solid #00f6ff;
  padding: 0;
}

/*模型样式*/
#model {
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
}
</style>